package aufgabe2;

import sun.security.acl.PermissionImpl;
import java.util.*;
import java.io.*;

/**
 * is needed to manage Course-objects, such as adding new courses and listing
 * them
 * 
 * @author Lukas Koell
 * @author David Medenjak
 * @author Johannes Riedmann
 */
public class UniManager implements java.io.Serializable {

    /**
     * Serialization
     */
    private static final long serialVersionUID = 1462796725139412649L;
    /** contains all added courses */
    private HashMap<Integer, Course> courses;
    /**
     * contains all dropped courses
     */
    private HashMap<Integer, Course> dropped_courses;
    /**
     * this is a singleton so we need an instance variable
     */
    private static UniManager instance = null;

    /**
     * Constructor creating the class.
     */
    private UniManager() {
        courses = new HashMap<Integer, Course>();
        dropped_courses = new HashMap<Integer, Course>();
    }

    /**
     * Implementing singleton class.
     * 
     * @return Returns the instance of this class
     */
    public static UniManager getInstance() {
        if (instance == null) {
            instance = new UniManager();
        }
        return instance;
    }

    /**
     * adds a course to the courses-Array
     * 
     * @param course
     *            that will be added
     */
    public void addCourse(Course course) throws CourseException {
        if (course == null) {
            throw new CourseException("No course selected.");
        }
        if (courses.containsKey(course.getCourseNr())
                || dropped_courses.containsKey(course.getCourseNr())) {
            throw new CourseException("Course with this number already listed.");
        }
        courses.put(course.getCourseNr(), course);
    }

    /**
     * drops a course from the course-Array, keeps it in "dropped_courses" array
     * 
     * @param course
     *            that will be dropped
     */
    public void dropCourse(Course course) throws CourseException {
        if (dropped_courses.containsKey(course.getCourseNr())) {
            throw new CourseException(
                    "Course with this number already in dropped listed.");
        }
        Course c = courses.remove(course.getCourseNr());
        if (c == null) {
            throw new CourseException("Course not listed.");
        }
        StudentNotification notification = new StudentNotification("Course "
                + course.getTitle() + " (" + course.getCourseNr()
                + ") was dropped.");

        course.notifyStudents(notification);
        dropped_courses.put(course.getCourseNr(), course);
    }

    /**
     * Restores a course from the course-Array
     * 
     * @param course
     *            that will be added again
     */
    public void restoreCourse(Course course) throws CourseException {
        if (dropped_courses.remove(course.getCourseNr()) == null) {
            throw new CourseException("Course not listed.");
        }
        addCourse(course);
    }

    /**
     * @return number of currently active courses
     */
    public int getNumberOfCourses() {
        return courses.size();
    }

    /**
     * lists all existing courses
     * 
     * @return a String list of existing courses if user privileges are
     *         insufficient
     * @throws NoAccessException
     */
    public String listCourses() throws AccessDeniedException {
        AccessManager.getInstance().checkAccess(
                new PermissionImpl("coursesList"));

        if (courses.isEmpty()) {
            return "No running courses.";
        }
        String list = new String();
        list += "Nr.\tTitel\t\tfr. Anmeldetermin\tsp. Anmeldetermin\tsp. "
                + "Abmeldetermin\tangemeldet\n";
        for (Course c : courses.values()) {
            list += c + "\n";
        }
        return list;
    }

    /**
     * lists all existing courses
     * 
     * @return a String list of existing courses
     */
    public String listDroppedCourses() {
        if (getDroppedCourses().isEmpty()) {
            return "No dropped courses.";
        }
        String list = new String();
        list += "Nr.\tTitel\t\tfr. Anmeldetermin\tsp. Anmeldetermin\tsp. Abmeldetermin\tangemeldet\n";
        for (int i = 0; i < getDroppedCourses().size(); i++) {
            list += getDroppedCourses().get(i) + "\n";
        }
        return list;
    }

    /**
     * @return the dropped_courses
     */
    public ArrayList<Course> getDroppedCourses() {
        Iterator<Course> it = dropped_courses.values().iterator();
        ArrayList<Course> dropped = new ArrayList<Course>();
        while (it.hasNext()) {
            dropped.add(it.next());
        }
        return dropped;
    }

    /**
     * serializes the course manager and writes it to disk
     * 
     * @param file
     *            in which the object will be stored
     */
    public void serialize(String file) {
        FileOutputStream f_out;
        try {
            f_out = new FileOutputStream(file);

            ObjectOutputStream obj_out;
            try {
                obj_out = new ObjectOutputStream(f_out);
                // Write object out to disk
                obj_out.writeObject(this);
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * loads a serialized UniManager object from disk and stores its courses and
     * dropped courses in the according instance variables
     * 
     * @param file
     *            the file to load the object from
     */
    public void unserialize(String file) {

        FileInputStream fis = null;
        ObjectInputStream in = null;
        try {
            fis = new FileInputStream(file);
            in = new ObjectInputStream(fis);
            UniManager loadedUm = (UniManager) in.readObject();

            this.courses = loadedUm.courses;
            this.dropped_courses = loadedUm.dropped_courses;
            in.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
    }
}
